package luzhin;

import java.io.*;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * Implements WinBoard protocol.
 * @author lacungus
 *
 * TODO rename to XBoard, with dependancies
 *
 */
public class WinBoardLuzhin {
    private static Logger logger = Logger.getLogger(WinBoardLuzhin.class);
	private static final String[] KEYWORDS = {"xboard", "protover", "accepted", "rejected", "new", "variant", "quit", "random", "force", "go", "playother", "white", "black", "level", "st", "sd", "nps", "time", "otim", "usermove", "?", "ping", "draw", "result", "setboard", "edit", "hint", "bk", "undo", "remove", "hard", "easy", "post", "nopost", "analyze", "name X", "rating", "ics HOSTNAME", "computer", "pause", "resume", "memory", "cores", "egtpath", "option", "post", "print"};
	
	private Board board;
	private Strategy strategy;
	
	private boolean forceModeOn = false;
    private Move move;

    static {
        PropertyConfigurator.configure("log4j.properties");
    }

	public WinBoardLuzhin(Strategy strategy) {
		this.strategy = strategy;
		BoardPool.initPool();
	}
	
	/*
	 * processing command from WinBoard
	 */
	private void process(String command) throws StrategyException {		
		command = command.trim();
		if (command.length() == 0)
			return;
		
		// true if it's actually command, not a move.
		boolean isCommand = false; 
		for (String s : KEYWORDS)
			if (command.contains(s)) {
				isCommand = true;
                break;
            }
		
		if (!isCommand) {
			processMove(command);
            return;
		}
		
		if (command.contains("quit"))
			System.exit(0);        
		if (command.contains("go")) {
			forceModeOn = false;
			makeMove();
			return;
		}
		if (command.contains("new")) {
			forceModeOn = false;
			board = new Board();
			return;
		}
		if (command.contains("force")) {
			forceModeOn = true;
			return;
		}
		if (command.contains("print")) {
			System.out.println(board);
			return;
		}
		if (command.contains("nopost")) {
			strategy.setIsThinkingOutputOn(false);
			return;
		}
		if (command.contains("post")) {
			strategy.setIsThinkingOutputOn(true);
			return;
		}
	}
	
	/*
	 * Makes a move according to the strategy and sending it to WinBoard.
	 */
	private void makeMove() throws StrategyException {
        Thread t = new Thread() {
            @Override
            public void run() {
                try {
                    move = strategy.makeMove(board);
                } catch (Exception e) {
                    e.printStackTrace();
                    move = null;
                }
            }
        };
        try {
            t.start();
            t.join();
        } catch (InterruptedException e) {
            throw new StrategyException("Thread was interrupted.");
        }
        // can't find any move: resign
		if (move == null) {
			System.out.println("resign");
			return;
		}
		
		System.out.println("move " + move.toString());
		board = board.applyMove(move);
	}
	
	/*
	 * Processing received move and making own if required.
	 */
	private void processMove(String command) throws StrategyException {
		move = new Move(command);
		board = board.applyMove(move);
		
		if (!forceModeOn) {
			makeMove();
		}
	}
	
	/*
	 * Start the engine, listen to commands.
	 */
	public void run() {
        logger.info("Luzhin started");
		strategy.setThinkingOutput(new PrintWriter(System.out));
		try {
			InputStreamReader ir = new InputStreamReader(System.in);
			String curS = "";
			while (true) {
				int c = ir.read();
				if (c != -1) {
					curS += (char) c;					
				}
				if (c == '\n') {
					process(curS);
					curS = "";
				}
			}
		}
		catch (Exception e) { 
			e.printStackTrace(System.out);
		}		
	}
	
	public static void main(String[] args) {
		WinBoardLuzhin wl = new WinBoardLuzhin(new BacktrackStrategy(new SimpleMoveGenerator(), new AdvancedEvaluator()));
		wl.run();
	}		
}
